package com.fjwt.gz.task.config.couponReceipt;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fjwt.gz.components.oss.model.MockMultipartFile;
import com.fjwt.gz.components.oss.model.OssFileConfig;
import com.fjwt.gz.components.oss.service.IOssService;
import com.fjwt.gz.components.oss.service.OssFileService;
import com.fjwt.gz.core.exception.BizException;
import com.fjwt.gz.core.model.DBOSSConfig;
import com.fjwt.gz.core.third.bo.CouponAwardBo;
import com.fjwt.gz.core.utils.SpringBeansUtil;
import com.fjwt.gz.coupon.hkpay.entity.HkBillResponseData;
import com.fjwt.gz.db.dto.AwardCouponDTO;
import com.fjwt.gz.db.entity.AwardCouponEntity;
import com.fjwt.gz.db.entity.FansAwardEntity;
import com.fjwt.gz.db.entity.OssRecordEntity;
import com.fjwt.gz.core.third.interfaces.ICheckService;
import com.fjwt.gz.service.impl.AwardCouponService;
import com.fjwt.gz.service.impl.OssRecordService;
import com.fjwt.gz.service.impl.SysConfigService;
import com.fjwt.gz.service.mapper.FansAwardMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;


/**
 * 定时任务对账单
 */
@Slf4j
@Component
public class AwardCouponCheckBillTask {

    private static final int QUERY_PAGE_SIZE = 100; //每次查询数量

    @Autowired
    private AwardCouponService awardCouponService;
    @Autowired
    private FansAwardMapper fansAwardMapper;
    @Autowired
    private OssRecordService ossRecordService;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private OssFileService ossFileService;

    private static final String TL_PAY = "tlpay";
    private static final String HK_PAY = "hkpay";
    private static final String WX_PAY = "wx";

    //    @Scheduled(cron="5 * * * * ?")
    public void start() {
        log.info("开始进入定时任务,对账单任务开始时间:{}", DateUtil.now());
        int currentPageIndex = 1; //当前页码
        while (true) {
            try {
                // 查询已结束活动的支付通道
                IPage<CouponAwardBo> couponsIpage = awardCouponService.selectOverAwardPayAuth(new Page(currentPageIndex, QUERY_PAGE_SIZE));
                log.info("对账单任务查询的卡券数据信息是：{}", JSONUtil.toJsonStr(couponsIpage));
                if (couponsIpage == null || CollUtil.isEmpty(couponsIpage.getRecords())) { //本次查询无结果, 不再继续查询;
                    break;
                }
                for (CouponAwardBo couponAwardBo : couponsIpage.getRecords()) {
                    if (StrUtil.isBlank(couponAwardBo.getPayConfig())) {
                        log.warn("出资方配置参数为空");
                        continue;
                    }
                    // 对应通道
                    String ifCode = couponAwardBo.getIfCode();
                    // 获取批次号
                    JSONObject payAuthConfigJson = JSONObject.parseObject(couponAwardBo.getPayConfig());
                    // 微信的 stockid    通联的 thirdActivityId   海科的stockId
                    Integer batchId = null;
                    switch (ifCode) {
                        case WX_PAY:
                            batchId = payAuthConfigJson.getInteger("stockid");
                            break;
                        case TL_PAY:
                            batchId = payAuthConfigJson.getInteger("thirdActivityId");
                            break;
                        case HK_PAY:
                            batchId = payAuthConfigJson.getInteger("stockId");
                            break;
                        default:
                            log.info("未有对应通道：{}", ifCode);
                    }
                    if (ObjectUtils.isEmpty(batchId)) {
                        log.info("卡券id{}，批次号为空", couponAwardBo.getCouponId());
                        continue;
                    }
                    log.info("活动批次号: {},此时处理的卡券数据信息是：{}", batchId, JSONUtil.toJsonStr(couponAwardBo));
                    ICheckService checkService = SpringBeansUtil.getBean(ifCode + "CheckBillService", ICheckService.class);
                    if (ObjectUtils.isEmpty(checkService)) {
                        log.info("未查询到对应通道：{}", ifCode);
                        continue;
                    }
                    // 从redis中获取通道配置信息
                    String authConfig = sysConfigService.getIfCode(ifCode);
                    if (ObjectUtils.isEmpty(authConfig) && !"wx".equals(ifCode)) {
                        log.info("未查询到通道配置信息：{}", ifCode);
                        continue;
                    }
                    couponAwardBo.setAuthConfig(authConfig);
                    JSONObject resultJson;
                    // 请求拉取账单，响应数据
                    JSONObject bill = checkService.getBill(couponAwardBo);
                    if (ObjectUtils.isEmpty(bill) || ObjectUtils.isEmpty(bill.get("billData"))) {
                        updateCouponAward(couponAwardBo.getCouponId(), null, null, ifCode + "拉取账单数据为空");
                        log.info("{}拉取账单数据为空", ifCode);
                        continue;
                    }
                    if (HK_PAY.equals(ifCode)) {
                        resultJson = checkBillHK(bill, couponAwardBo);
                    } else {
                        BufferedReader reader = (BufferedReader) bill.get("billData");
                        // 对账
                        resultJson = checkBill(reader, couponAwardBo);
                    }
                    if (ObjectUtils.isEmpty(resultJson.get("response"))) {
                        updateCouponAward(couponAwardBo.getCouponId(), null, null, ifCode + "对账结果为空");
                        log.info("{}拉取账单数据为空", ifCode);
                        continue;
                    }

                    StringBuilder response = (StringBuilder) resultJson.get("response");
                    log.info("Response: {}", JSONUtil.toJsonStr(response));

                    log.info("Response: {}", JSONUtil.toJsonStr(response));

                    String fileDirPath = System.getProperty("user.dir") + File.separator
                            + String.format("verifyBill/%s", ifCode);
                    Path dirPath = Paths.get(fileDirPath);
                    Files.createDirectories(dirPath); // 确保目录存在
                    Path filePath = dirPath.resolve(batchId + ".csv");
                    log.info("本地文件路径: {}", filePath);

                    try (BufferedWriter writer = Files.newBufferedWriter(filePath)) {
                        writer.write(response.toString());
                    }
                    MultipartFile multipartFile = new MockMultipartFile(filePath.toFile().getPath(), filePath.toFile().length(), new FileInputStream(filePath.toFile()));
                    // OSS文件上传
                    String ossFileUrl = ossFileService.singleFileUpload(multipartFile, OssFileConfig.BIZ_TYPE.UPLOAD);
                    log.info("通联对账OSS文件存储路径: {}", JSONUtil.toJsonStr(ossFileUrl));
                    // 添加oos上传记录
                    OssRecordEntity ossRecordEntity = new OssRecordEntity();
                    ossRecordEntity.setBizType(OssFileConfig.BIZ_TYPE.UPLOAD);
                    ossRecordEntity.setOssUrl(ossFileUrl);
                    ossRecordEntity.setFileSize(filePath.toFile().length());
                    ossRecordEntity.setUploadType(5);
                    ossRecordEntity.setCreatedAt(new Date());
                    ossRecordService.save(ossRecordEntity);
                    log.info("OSS表新增信息: {}", JSONUtil.toJsonStr(ossRecordEntity));

                    // 卡券信息修改
                    updateCouponAward(couponAwardBo.getCouponId(), resultJson.getInteger("state"), ossRecordEntity.getOssRecordId(),
                            "共" + resultJson.get("total") + "条数据,对账成功" + resultJson.get("successTotal") + "条,对账失败" + resultJson.get("failTotal") + "条");
                }

                //已经到达页码最大量，无需再次查询
                if (couponsIpage.getPages() <= currentPageIndex) {
                    break;
                }
                currentPageIndex++;
            } catch (BizException e) { //出现异常，直接退出，避免死循环。
                log.error("error", e.getMessage());
                break;
            } catch (Exception e) { //出现异常，直接退出，避免死循环。
                log.error("error", e);
                break;
            }
        }
    }

    /**
     * 对账流程
     *
     * @param reader        拉取账单数据
     * @param couponAwardBo
     * @return
     */
    private JSONObject checkBill(BufferedReader reader, CouponAwardBo couponAwardBo) {
        // 返回结果
        JSONObject resultJson = new JSONObject();
        // 对账结果 4:成功 5:失败
        int state = 4;
        // 总条数
        int total = 0;
        // 对账成功条数
        int successTotal = 0;
        // 对账失败条数
        int failTotal = 0;
        // 响应内容
        StringBuilder response = new StringBuilder();

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        try (reader) {  // 自动关闭资源
            String line;
            while ((line = reader.readLine()) != null) {
                line = line.replaceAll("`", "");
                if (response.isEmpty()) {
                    line = line + ",是否对账成功,系统卡券ID,失败原因,对账时间";
                    log.info("对账开始，表头信息为: {}", JSONUtil.toJsonStr(line));
                    response.append(line).append("\r");
                    continue;
                }
                if ("总条数".equals(line)) {
                    response.append(line).append(",").append(failTotal + successTotal);
                    break;
                }
                String[] split = line.split(",");
                if (split.length < 9) {
                    log.info("对账失败，数据格式错误: {}", JSONUtil.toJsonStr(line));
                    // 数据格式错误处理
                    line = line + ",失败, ,数据格式错误," + DateUtil.now();
                    state = 5;
                    failTotal++;
                    response.append(line).append("\r");
                    continue;
                }
                total = total + 1;
                //微信和通联返回数据 0：批次id，1：优惠id，2：优惠类型，3：优惠金额（元），4：订单总金额（元），5：交易类型，6：支付单号，7：消耗时间，8：消耗商户号， 9：设备号，10：银行流水号，11：单品信息
                if (StringUtils.isAnyBlank(split[1], split[7], split[8])) {
                    line = line + ",失败, ,数据不全无法对账," + DateUtil.now();
                    state = 5;
                    failTotal++;
                    response.append(line).append("\r");
                    continue;
                }

                // 查询数据库
                LambdaQueryWrapper<FansAwardEntity> fansAwardGw = FansAwardEntity.gw();
                fansAwardGw.eq(FansAwardEntity::getCouponThirdId, split[1]);
                FansAwardEntity fansAwardEntity = fansAwardMapper.selectOne(fansAwardGw);
                if (ObjectUtils.isEmpty(fansAwardEntity)) {
                    line = line + ",失败, ,系统中无法查到该数据," + DateUtil.now();
                    state = 5;
                    failTotal++;
                    response.append(line).append("\r");
                    continue;
                }

                // 修改会员奖品表中卡券状态、核销商户、核销时间信息
                try {
                    LocalDateTime writeTime = LocalDateTime.parse(split[7], formatter);
                    fansAwardEntity.setState(4);
                    fansAwardEntity.setMchNo(split[8]);
                    fansAwardEntity.setWriteTime(Date.from(writeTime.atZone(ZoneId.systemDefault()).toInstant()));
                    fansAwardEntity.setUpdatedAt(new Date());
                    fansAwardMapper.updateById(fansAwardEntity);
                    line = line + ",成功," + couponAwardBo.getCouponId() + ", ," + DateUtil.now();
                    successTotal++;
                } catch (Exception e) {
                    // 日期格式错误处理
                    line = line + ",失败, ,时间格式错误," + DateUtil.now();
                    state = 5;
                    failTotal++;
                    log.error("日期解析失败: {}", split[7], e);
                }

                response.append(line).append("\r");
            }
        } catch (Exception e) {
            // 记录详细错误日志
            log.error("对账处理异常", e);
            // 设置全局失败状态
            state = 5;
            // 记录异常信息到响应
            response.append("系统异常: ").append(e.getMessage());
        }

        // 统一结果封装
        resultJson.put("response", response);
        resultJson.put("state", state);
        resultJson.put("total", total);
        resultJson.put("successTotal", successTotal);
        resultJson.put("failTotal", failTotal);
        return resultJson;
    }

    private JSONObject checkBillHK(JSONObject checkData, CouponAwardBo awardBo) {
        JSONObject result = new JSONObject();
        //对账结果 4：成功  5：失败
        int state = 4;
        int total = 0, success = 0, fail = 0;
        total = checkData.getInteger("totalCount");
        JSONArray billData = checkData.getJSONArray("data");
        StringBuilder response = new StringBuilder();

        List<HkBillResponseData> hkBillResponseDataList = JSON.parseObject(billData.toJSONString(),
                new TypeReference<List<HkBillResponseData>>() {
                });

        String header = "卡券ID,优惠类型,优惠金额,订单总金额,交易类型,支付单号,消耗时间,消耗商户号,设备号" +
                ",银行流水号,是否对账成功,系统卡券ID,失败原因,对账时间";
        response.append(header).append("\r");

        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        for (HkBillResponseData data : hkBillResponseDataList) {
            StringBuilder line = new StringBuilder();
            if (StringUtils.isAnyBlank(data.getConsumeMerchantId(), data.getCouponId(), data.getConsumeTime())) {
                state = 5;
                fail++;
                line.append(data).append(",失败,,数据不全无法对账,").append(DateUtil.now());
                response.append(line).append("\r");
                continue;
            }
            //查询系统中是否存在该卡券
            FansAwardEntity entity = fansAwardMapper.selectOne(
                    FansAwardEntity.gw()
                            .eq(FansAwardEntity::getCouponThirdId, data.getCouponId())
            );
            if (entity == null) {
                line.append(data).append(",失败,,系统中无法查到该数据,").append(DateUtil.now());
                response.append(line).append("\r");
                state = 5;
                fail++;
                continue;
            }
            // 修改会员奖品表中卡券状态、核销商户、核销时间信息
            try {
                Date writeTime = formatter.parse(data.getConsumeTime());
                entity.setState(4);
                entity.setMchNo(data.getConsumeMerchantId());
                entity.setWriteTime(writeTime);
                entity.setUpdatedAt(new Date());
                fansAwardMapper.updateById(entity);
                line.append(data).append(",成功,").append(awardBo.getCouponId()).append(", ,").append(DateUtil.now());
                response.append(line).append("\r");
                success++;
            } catch (ParseException e) {
                line.append(data).append(",失败,,日期格式错误,").append(DateUtil.now());
                response.append(line).append("\r");
                state = 5;
                fail++;
            }
        }

        result.put("response", response);
        result.put("state", state);
        result.put("total", total);
        result.put("successTotal", success);
        result.put("failTotal", fail);
        return result;
    }


    /**
     * 卡券信息修改
     */
    public void updateCouponAward(Long couponId, Integer state, Long billOssRecordId, String billErrorMsg) {
        AwardCouponEntity couponAward = awardCouponService.getById(couponId);
        if (!ObjectUtils.isEmpty(state)) {
            couponAward.setState(state);
        }
        couponAward.setBillOssRecordId(billOssRecordId);
        couponAward.setBillErrorMsg(billErrorMsg);
        awardCouponService.updateById(couponAward);
        log.info("卡券更改信息: {}", JSONUtil.toJsonStr(couponAward));
    }

    /**
     * 上传文件到oss
     */
    public String uploadToOss(File file, Integer couponId) {
        DBOSSConfig ossConfig = sysConfigService.getOssConfig();
        IOssService iOssService = SpringBeansUtil.getBean(ossConfig.getOssUseType() + "OssService", IOssService.class);
        IOssService.UploadFormParams uploadFormParams = iOssService.genFormParams(OssFileConfig.BIZ_TYPE.DOWNLOAD, OssFileConfig.getOssFileConfigByBizType(OssFileConfig.BIZ_TYPE.DOWNLOAD), OssFileConfig.BIZ_TYPE.DOWNLOAD + "/" + couponId + ".csv");
        try {
            HttpResponse httpResponse = HttpRequest.post(uploadFormParams.getFormActionUrl())
                    .header("Content-Type", "multipart/form-data")
                    .timeout(20000)
                    .form("signature", uploadFormParams.getFormParams().getString("signature"))
                    .form("x-obs-acl", uploadFormParams.getFormParams().getString("x-obs-acl"))
                    .form("content-type", uploadFormParams.getFormParams().getString("content-type"))
                    .form("key", uploadFormParams.getFormParams().getString("key"))
                    .form("policy", uploadFormParams.getFormParams().getString("policy"))
                    .form("accesskeyid", uploadFormParams.getFormParams().getString("accesskeyid"))
                    .form("OSSAccessKeyId", uploadFormParams.getFormParams().getString("OSSAccessKeyId"))
                    .form("file", file)
                    .execute();
            if (org.apache.commons.lang3.ObjectUtils.isEmpty(httpResponse) || 204 != httpResponse.getStatus()) {
                throw new BizException("文件上传oss失败");
            }
        } catch (Exception e) {
            throw new BizException("文件上传oss失败");
        } finally {
            FileUtil.del(file);
        }
        return uploadFormParams.getOssFileUrl();
    }
}
